package com.p2p.Androidp2p;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.zip.CRC32;

import android.util.Log;

public class Database {

    private static String serverAddress = "http://csil-projects.cs.uiuc.edu/~sretika2/GermanShepherd.php";
    private static int retryLimit = 5;
    private static String myUsername = "a";


    public static String calculateChecksum(String filePath) {
	File testFile = new File(filePath);
	try
	{
	    testFile.createNewFile();
	    FileInputStream inputStream = new FileInputStream(testFile);

	    CRC32 theCRC = new CRC32();

	    while (inputStream.available() > 0) {
		theCRC.update(inputStream.read());
	    }

	    Log.d("Checksum", ""+theCRC.getValue());
	    return ""+theCRC.getValue(); 

	} 
	catch (IOException e)
	{
	    e.printStackTrace();
	}
	
	return null;
    }

    /**
     * Will establish a connection with an http server to be used for read/write
     * @param address the server address
     * @return the connection on success, null otherwise
     */
    private static URLConnection openHttpConnection(String address) {

	try {
	    URL url = new URL(serverAddress);
	    URLConnection conn = url.openConnection();
	    conn.setDoOutput(true);
	    conn.setDoInput(true);
	    conn.setUseCaches(false);
	    conn.setDefaultUseCaches(false);  

	    return conn;
	} 
	catch (Exception e) {
	    return null;
	}
    }

    /**
     * This function will perform a query to the http server
     * Will keep retrying on failed query
     * 
     * To register, actionType:'register', actionParams:
     * <Register>
     * <Username>ychodpa2</Username>
     * <Port>2222</Port>
     * <File>
     *	<Name>filename</Name>
     *	<Size>1024</Size>
     * </File>
     * </Register>
     * Response : <Status>SUCCESS</Status>
     *
     *
     * To search for a file, actionType:'getFileList', actionParams:
     * <getFileList>name</getFileList>
     * Response : <FileList>
     * 					<File>
     * 						<ID>id</ID>
     *						<Name>filename</Name>
     *						<Size>1024</Size>
     *					</File>
     *				</FileList>

     *
     * To get file holders, actionType:'getHolderList', actionParams:
     * <getHolderList>FileID</getHolderList>	
     * Response : <HolderList>
     * 				<Holder>
     * 					<Name>username</Name>
     * 					<Ip>ip</Ip>
     * 					<Port>port</Port>
     * 				</Holder>
     * 			  </HolderLiset>
     * 
     * 

     * @param actionType the type of service
     * @param actionParams the data to send (xml specific to the service)
     * @return byte[] of the response on success, null otherwise
     */
    public static byte[] queryServer(String actionType, String actionParams) {

	if (actionType == null || actionParams == null) {
	    writeToLog("queryServer", "bad request string");
	    return null;
	}
	writeToLog("queryServer", "querying...");
	HttpURLConnection conn = (HttpURLConnection) openHttpConnection(serverAddress);

	//Construct data values
	String encodedData;

	try {
	    byte[] retval = null;

	    int numRetry = 1;
	    while (numRetry < retryLimit && (retval == null || retval.length <= 0)) {

		writeToLog("queryServer", "Attempt no " + numRetry);
		//Encode in http POST
		encodedData =  URLEncoder.encode("username", "UTF-8") + "=" + URLEncoder.encode(myUsername, "UTF-8");

		encodedData += "&" + URLEncoder.encode("queryType", "UTF-8") + "=" + URLEncoder.encode(actionType, "UTF-8");
		encodedData += "&" + URLEncoder.encode("queryParam", "UTF-8") + "=" + URLEncoder.encode(actionParams, "UTF-8");

		//Send the query 
		conn.connect();
		OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
		wr.write(encodedData);
		wr.flush();

		writeToLog("queryServer", "Successfully sent : " + actionType + "@" + actionParams);
		wr.close();

		//Start receiving response
		retval = readFromServer(conn);

		numRetry++;
	    }
	    return retval; 
	} 
	catch (UnknownHostException e) {
	    writeToLog("queryServer", "Unknown host error");
	    if (e != null)
		writeToLog("queryServer", String.valueOf(e.getMessage()));
	}
	catch (UnsupportedEncodingException e) {
	    writeToLog("queryServer", "encoding error");
	    if (e != null)
		writeToLog("queryServer", String.valueOf(e.getMessage()));
	}
	catch (IOException e) {
	    writeToLog("queryServer", "IO error");
	    if (e != null)
		writeToLog("queryServer", String.valueOf(e.getMessage()));
	}
	finally {
	    conn.disconnect();
	}

	return null;
    }

    private static void writeToLog(String string, String string2) {
	//Log.d(String.valueOf(string), String.valueOf(string2));
    }

    /**
     * Will listen for reponse from the server
     * @param conn the server connection
     * @return the byte[] of data on success, null otherwise
     */
    private static byte[] readFromServer(URLConnection conn) {

	//Get the response
	try {
	    writeToLog("readFromServer", "reading from server...");

	    BufferedInputStream inputStream = new BufferedInputStream(conn.getInputStream());
	    int length = conn.getContentLength();

	    //Field length is set by the server
	    if (length != -1) 
	    {
		byte[] buf = new byte[length];
		int bytesRead = inputStream.read(buf);

		//Failed reading
		if (bytesRead <= 0) {
		    writeToLog("readFromServer", "cannot read from server");
		    return null;
		}
		//Successful reading
		else {
		    writeToLog("readFromServer", "read " + bytesRead + " bytes");
		    return buf;
		}
	    } 
	    //Field length is not set, listen until -1
	    else {
		//Hold the buffer in this temporary dynamic arraylist first
		ArrayList<Byte> buf = new ArrayList<Byte>();
		int thisByte;
		int bytesRead = 0;

		while ((thisByte = inputStream.read()) != -1) { 
		    buf.add((byte) thisByte);
		    bytesRead++;
		}

		//Failed reading
		if (bytesRead <= 0) 
		    return null;

		writeToLog("readFromServer", "read " + bytesRead + " bytes");

		//Convert the temporary dynamic buffer to static byte[] to return
		byte[] retval = new byte[buf.size()];
		for (int i=0; i<buf.size(); i++) 
		    retval[i] = buf.get(i).byteValue();

		return retval; 
	    }
	} 

	catch (IOException e) {
	    writeToLog("readFromServer", "io exception");
	}

	return null;
    }
}
